package cn.jilian.meta.service.impl;

import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

import cn.jilian.common.dto.FilterPageSortQuery;
import cn.jilian.common.dto.PageResult;
import cn.jilian.common.dto.SortInfo;
import cn.jilian.common.util.MapUtil;
import cn.jilian.dao.AttrDAO;
import cn.jilian.dao.MFieldDAO;
import cn.jilian.dao.MetaDAO;
import cn.jilian.dao.PageQuery;
import cn.jilian.data.util.DataConverter;
import cn.jilian.entity.Attr;
import cn.jilian.entity.MField;
import cn.jilian.entity.Meta;
import cn.jilian.meta.service.MetaService;
import cn.jilian.meta.service.domain.SimpleList;

@Service
public class MetaServiceImpl implements MetaService {

	@Autowired
	private EntityManager entityManager;
	@Autowired
	private MetaDAO metaDAO;
	
	private Multimap<Long,Long> mainTraitMap=HashMultimap.<Long, Long>create();
	@Autowired
	private ObjectMapper objectMapper;
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	@Autowired
	private PageQuery pageQuery;
	
	@Autowired
	private MFieldDAO fieldDAO;
	@Autowired
	private AttrDAO attrDAO;
	
	@Cacheable(value="metaInfoCache")
	@Override
	@Transactional
	public Meta getMetaById(long id) {
		Meta meta=metaDAO.findOne(id);
		return meta;
	}
	
	@Transactional
	@CacheEvict(value="metaInfoCache",key="#meta.getId()")
	@Override
	public int updateMeta(Meta meta) {
		entityManager.merge(meta);
		return 0;
	}
	@Override
	public Meta addMeta(Meta meta) {
		if(meta.getSeq()==null||meta.getSeq()<=0) {
			Integer seq=metaDAO.countByFidAndRank(meta.getFid(), meta.getRank());
			meta.setSeq(seq);
		}
		return metaDAO.save(meta);
	}
	@Override
	public int deleteMeta(long metaId) {
		metaDAO.delete(metaId);
		return 1;
	}

	@Override
	public Meta queryMetaByName(String name) {
		return metaDAO.findByCode(name);
	}

	@Override
	@Transactional
	public List<Meta> querySubMetas(Long id) {
		return metaDAO.findByFidOrderBySeq(id);
	}

	@Override
	public void registToMainModel(Long metaId,Long mainModelId) {
		mainTraitMap.put(metaId, mainModelId);
		
	}

	@Override
	public Collection<Long> getTraitModelIds(Long id) {
		return mainTraitMap.get(id);
	}
	
	@SuppressWarnings("unchecked")
	private void putFullMeta(Meta meta,Map<Long,Object> refMetaMap,Map<Long,SimpleList> enumMap) {
		refMetaMap.put(meta.getId(), meta);
		Map<String,Object> metaMap=(Map<String, Object>) meta.getAttr("meta");
		if(metaMap!=null) {
			for(Map.Entry<String, Object> e:metaMap.entrySet()) {
				Object v=e.getValue();
				if(Number.class.isAssignableFrom(v.getClass())) {
					Long metaId=objectMapper.convertValue(e.getValue(),Long.class);
					if(!refMetaMap.containsKey(metaId)) {
						putFullMetaById(metaId,refMetaMap,enumMap);
					}
					
				}else if(Map.class.isAssignableFrom(v.getClass())){
					Map<String,Object> m=(Map<String,Object>)v;
					for(Map.Entry<String, Object> e1:m.entrySet()) {
						Long metaId=objectMapper.convertValue(e1.getValue(),Long.class);
						if(!refMetaMap.containsKey(metaId)) {
							putFullMetaById(metaId,refMetaMap,enumMap);
						}
					}
				}
				
			}
			
		}
		List<Meta> cs=metaDAO.findByFidOrderBySeq(meta.getId());
		if(CollectionUtils.isEmpty(cs)) {
			meta.setChildren( Collections.emptyList());
		}else {
			List<Long> rs=new ArrayList<>();
			meta.setChildren(rs);
			for(Meta m:cs) {
				rs.add(m.getId());
				putFullMeta(m,refMetaMap,enumMap);
			}
			
		}
		List<MField> fields=meta.getFields();
		for(MField field:fields) {
			if(field.containsAttr(MField.ENUMS)) {
				Long enums=objectMapper.convertValue(field.getAttr(MField.ENUMS), Long.class);
				if(!enumMap.containsKey(enums)) {
					SimpleList list= getEnumListById(enums);
					enumMap.put(enums, list);
				}
			}
			if(field.containsAttr(MField.REFMODELID)) {
				Long refModelId=objectMapper.convertValue(field.getAttr(MField.REFMODELID), Long.class);
				if(!refMetaMap.containsKey(refModelId)) {
					putFullMetaById(refModelId,refMetaMap,enumMap);
				}
			}
		}
		Long proMetaId=meta.getMetaId();
		if(proMetaId!=null&&!refMetaMap.containsKey(proMetaId)) {
			putFullMetaById(proMetaId,refMetaMap,enumMap);
		}
	}
	
	private void putFullMetaById(long id,Map<Long,Object> refMetaMap,Map<Long,SimpleList> enumMap) {
		Meta meta=getMetaById(id);
		if(meta!=null) {
			putFullMeta(meta,refMetaMap,enumMap);
		}
	}

	@Override
	@Transactional
	public Map<String, Object> getFullMetaById(Long id) {
		Meta meta=getMetaById(id);
		if(meta!=null) {
			Map<Long,Object> refMetaMap=new HashMap<>();
			Map<Long,SimpleList> enumMap=new HashMap<>();
			Map<String,Object> fullMeta=new HashMap<>();
			fullMeta.put("id", id);
			fullMeta.put("refMetaMap", refMetaMap);
			fullMeta.put("refEnumMap", enumMap);
			putFullMeta(meta,refMetaMap,enumMap);
			return fullMeta;
		}
		
		return null;
	}

	public SimpleList getEnumListByMeta(Meta meta) {
		try {
			
			String table=meta.getName();
			String sql=null;
			if(!meta.getMetaId().equals(Meta.ENUM_META)) {
				table="bt_meta";
				 sql=new StringBuilder("select * from ")
						.append(table).append(" where mid=? and meta not in(")
						.append(Meta.VIEW_META)
						.append(")")
						.toString();
			}else {
				sql=new StringBuilder("select * from ")
						.append(table).append(" where mid=?").toString();
			}
			
			List<Map<String,Object>> list=jdbcTemplate.queryForList(sql, meta.getId());
			return new SimpleList(list);
		}catch(Exception e) {
			e.printStackTrace();
			return new SimpleList(Collections.emptyList());
		}
	}
	@Override
	public SimpleList getEnumListByName(String name) {
		try {
			Meta meta=queryMetaByName(name);
			if(meta==null)return new SimpleList(Collections.emptyList());
			return getEnumListByMeta(meta);
		}catch(Exception e) {
			e.printStackTrace();
			return new SimpleList(Collections.emptyList());
		}
	}
	
	
	
	@Override
	public SimpleList getEnumListById(Long id) {
		try {
			Meta meta=getMetaById(id);
			if(meta==null)return new SimpleList(Collections.emptyList());
			return getEnumListByMeta(meta);
		}catch(Exception e) {
			e.printStackTrace();
			return new SimpleList(Collections.emptyList());
		}
	}

	@Override
	public int deleteRecord(Long id, Long metaId) {
		Meta meta=getMetaById(metaId);
		MField f=meta.getPKField();
		Map<String,Object> record=getRecord(metaId,id);
		String sql=new StringBuilder("delete from ").append(meta.getName())
				.append(" where ").append(f.getAttr(MField.STORECOLUMN,f.getName()))
				.append(" =?").toString();
		if(meta.getAttr(Meta.ORDERSEQPRPERTY)!=null) {
			String seqProperty=(String) meta.getAttr(Meta.ORDERSEQPRPERTY);
			String seqColumn=meta.getField(seqProperty).getAttr(MField.STORECOLUMN, "seq");
			String fidProperty=meta.getAttr(Meta.PARENTIDPROPERTY,Meta.PARENTIDPROPERTY_DEFAULT);
			String fidColumn=meta.getField(fidProperty).getAttr(MField.STORECOLUMN, "fid");
			StringBuilder sb=new StringBuilder("update ")
					.append(meta.getName())
					.append(" set ")
					.append(seqColumn)
					.append("=")
					.append(seqColumn)
					.append("-1 where ")
					.append(fidColumn)
					.append("=? ");
			String moduleProperty=(String) meta.getAttr(Meta.MODULE_PROPERTY);
			if(moduleProperty!=null) {
				String rankColumn=meta.getField(moduleProperty).getAttr(MField.STORECOLUMN, "rank");
				sb.append(" and ")
				.append(rankColumn)
				.append("=").append(meta.getId());
			}
			sb.append(" and ")
			.append(seqColumn)
			.append(">?");
			jdbcTemplate.update(sb.toString(),record.get(fidProperty),record.get(seqProperty));
		}
		return jdbcTemplate.update(sql,id);
	}

	@Override
	public int moveRecord(Long metaId, Long id, Long fid, Long seq) {
		Meta meta=getMetaById(metaId);
		String parentIdProperty=meta.getAttr(Meta.PARENTIDPROPERTY,Meta.PARENTIDPROPERTY_DEFAULT);
		MField fidField=meta.getField(parentIdProperty);
		String fidColumn=fidField.getAttr(MField.STORECOLUMN,fidField.getName());
		MField idField=meta.getPKField();
		String idColumn=idField.getAttr(MField.STORECOLUMN, idField.getName());
		String seqProperty=meta.getAttr(Meta.ORDERSEQPRPERTY, "seq");
		MField seqField=meta.getField(seqProperty);
		String seqColumn=(String) seqField.getAttr(MField.STORECOLUMN, seqField.getName());
		String moduleProperty=(String) meta.getAttr(Meta.MODULE_PROPERTY);
		Map<String,Object> record=getRecord(metaId,id);
		Long oldFid=((Number) record.get(parentIdProperty)).longValue();
		Long oldSeq=((Number)record.get(seqProperty)).longValue();
		String table=meta.getName();
		StringBuilder svb=new StringBuilder("select max(").append(seqColumn)
				.append(") as max from ")
				.append(table)
				.append(" where ")
				.append(fidColumn)
				.append("=?");
		Boolean hasModuleProperty=false;
		
		if(StringUtils.isNotBlank( moduleProperty )) {
			hasModuleProperty=true;
			
			svb.append(" and ")
			.append(moduleProperty)
			.append("=").append(meta.getId());
		}
		String sql=svb.toString();
		if(oldFid.equals(fid)) {
			if(!oldSeq.equals(seq)) {
				sql=new StringBuilder("update ")
						.append(table)
						.append(" set ")
						.append(seqColumn)
						.append("=")
						.append(seqColumn)
						.append("+? where ")
						.append(fidColumn)
						.append("=? and ")
						.append(seqColumn)
						.append(">? and ")
						.append(seqColumn)
						.append("<?")
						.toString();
				
				if(oldSeq.compareTo(seq)>0) {
					jdbcTemplate.update(sql,1,fid,seq,oldSeq);
					
				}else {
					jdbcTemplate.update(sql,-1,fid,oldSeq,seq);
				}
				
			}
		}else {
			StringBuilder sb=new StringBuilder("update ")
					.append(table)
					.append(" set ")
					.append(seqColumn)
					.append("=")
					.append(seqColumn)
					.append("+? where ")
					.append(fidColumn)
					.append("=? and ")
					.append(seqColumn)
					.append(">?");
			if(hasModuleProperty) {
				sb.append(" and ")
				.append(moduleProperty)
				.append("=")
				.append(meta.getId());
			}
			sql=sb.toString();
			jdbcTemplate.update(sql, -1,oldFid,oldSeq);
			jdbcTemplate.update(sql, 1,fid,seq);
			
		}
		sql=new StringBuilder("update ")
				.append(table)
				.append(" set ")
				.append(fidColumn)
				.append("=?,")
				.append(seqColumn)
				.append("=? where ")
				.append(idColumn)
				.append("=?").toString();
		jdbcTemplate.update(sql,fid,seq,id);
		return 1;
	}
	

	@Override
	public int moveRecord(Long metaId, Long id, int step) {
		Meta meta=getMetaById(metaId);
		String parentIdProperty=meta.getAttr(Meta.PARENTIDPROPERTY,Meta.PARENTIDPROPERTY_DEFAULT);
		MField fidField=meta.getField(parentIdProperty);
		String fidColumn=fidField.getAttr(MField.STORECOLUMN,fidField.getName());
		MField idField=meta.getPKField();
		String idColumn=idField.getAttr(MField.STORECOLUMN, idField.getName());
		String seqProperty=meta.getAttr(Meta.ORDERSEQPRPERTY, "seq");
		MField seqField=meta.getField(seqProperty);
		String seqColumn=(String) seqField.getAttr(MField.STORECOLUMN, seqField.getName());
		String moduleProperty=(String) meta.getAttr(Meta.MODULE_PROPERTY,"rank");
		MField moduleField=meta.getField(moduleProperty);
		String moduleColumn=moduleField!=null?moduleField.getAttr(MField.STORECOLUMN, "rank"):"rank";
		Map<String,Object> record=getRecord(metaId,id);
		String sql=new StringBuilder("select count(1) as max from ")
				.append(meta.getName())
				.append(" where ")
				.append(fidColumn)
				.append(" =? and ")
				.append(moduleColumn)
				.append("=")
				.append(meta.getId())
				.toString();
		Long max=jdbcTemplate.queryForObject(sql, new Object[] {record.get("FID")},
				Long.class);
		Long fid=((Number)record.get(fidField.getName())).longValue();
		Long oldSeq=((Number)record.get("SEQ")).longValue();
		Long newSeq=(max+step+oldSeq)%max;
		sql=new StringBuilder("update ")
				.append(meta.getName())
				.append(" set ")
				.append(seqColumn)
				.append("=")
				.append(seqColumn)
				.append("+? where ")
				.append(fidColumn)
				.append("=? and ")
				.append(seqColumn)
				.append(">=? and ")
				.append(seqColumn)
				.append("<=?")
				.toString();
		int rs=oldSeq.compareTo(newSeq);
		
		if(rs==0) {
			return 0;
		}
		if(rs>0) {
			jdbcTemplate.update(sql,1,fid,newSeq,oldSeq);
		}else {
			jdbcTemplate.update(sql,-1,fid,oldSeq,newSeq);
		}
		sql=new StringBuilder("update ").append(meta.getName())
				.append(" set ")
				.append(seqColumn)
				.append("=? where ")
				.append(idColumn)
				.append("=?")
				.toString();
		jdbcTemplate.update(sql,newSeq,id);
		return 1;
	}

	@Override
	public Map<String, Object> getRecord(Long metaId, Long id) {
		Meta meta=getMetaById(metaId);
		return getRecord(meta,id);
	}

	@Override
	public PageResult<Map<String, Object>> pageQuery(Long metaId, FilterPageSortQuery query) {
		Meta meta=getMetaById(metaId);
		return pageQuery(meta,query);
	}
	
	@SuppressWarnings("unchecked")
	private String getFilterSql(StringBuilder sb,Meta meta,Map<String,Object> filterMap,List<Object> args) {
			sb.append(" where ");
			for(Map.Entry<String, Object> e:filterMap.entrySet()) {
				Object v=e.getValue();
				if(v!=null&&!"".equals(v)) {
					MField f=meta.getField(e.getKey());
					Integer type=f.getType();
					String storeColumn=(String) f.getAttr(MField.STORECOLUMN, f.getName());
					if(type.equals(Types.CHAR)||type.equals(Types.VARCHAR)) {
						String value=(String) DataConverter.convert(v, type);
						if(StringUtils.isNotBlank(value)) {
							sb.append(storeColumn);
							sb.append(" like ? and ");
							args.add("%"+value+"%");
						}
					}else if(type.equals(Types.DATE)||type.equals(Types.TIMESTAMP)) {
						if(Map.class.isAssignableFrom(v.getClass())) {
							Map<String,Object> range=(Map<String,Object>)v;
							if(range.containsKey("start")) {
								String start=(String) range.get("start");
								if(StringUtils.isNotBlank(start)) {
									sb.append(storeColumn).append(" >=? and ");
									args.add(start);
								}
							}
							if(range.containsKey("end")) {
								String end=(String) range.get("end");
								if(StringUtils.isNotBlank(end)) {
									sb.append(storeColumn).append(" <=? and ");
									args.add(end);
								}
							}
						}
					}
					else {
						sb.append(storeColumn);
						sb.append(" =? and ");
									args.add(DataConverter.convert(e.getValue(), f.getType()));
					}
				}
				
			}
			sb.setLength(sb.length()-5);
			
			return sb.toString();
		
	}

	@Override
	public int addRecord(Long metaId, Map<String, Object> params) {
		if(params==null)return 0;
		Meta meta=getMetaById(metaId);
		if(meta==null) {
			return 0;
		}
		return addRecord(meta,params);
	}

	@Override
	public int updateRecord(Long metaId, Map<String, Object> record) {
		if(record==null)return 0;
		Meta meta=getMetaById(metaId);
		if(meta==null) {
			return 0;
		}
		return updateRecord(meta,record);
	}

	@Override
	public PageResult<Map<String, Object>> pageQuery(Meta meta, FilterPageSortQuery query) {

		StringBuilder sb=new StringBuilder("select ");
		List<MField> fs=meta.getFields();
		if(CollectionUtils.isNotEmpty(fs)) {
			for(MField f:fs) {
				sb.append(f.getAttr(MField.STORECOLUMN, f.getName()))
				.append(" AS '").append(f.getName()).append("',");
			}
			sb.setLength(sb.length()-1);
		}else{
			sb.append("*");
		}
		sb.append(" from ").append(meta.getName());
		Map<String,Object> filterMap=query.getFilter();
		SortInfo sortInfo=query.getSortInfo();
		if(sortInfo!=null) {
			MField f=meta.getField(sortInfo.getField());
			sortInfo.setField((String) f.getAttr(MField.STORECOLUMN, f.getName()));
		}
		if(filterMap!=null&&filterMap.size()>0) {
			List<Object> args=new ArrayList<>();
			String sql=getFilterSql(sb,meta,filterMap,args);
			return pageQuery.query(sql, args.toArray(), query.getPageInfo(),sortInfo);
		}else {
			return pageQuery.query(sb.toString(), null, query.getPageInfo(),sortInfo);
		}
	}

	@Override
	public int addRecord(Meta meta, Map<String, Object> params) {
		if(meta.containsKey(Meta.ORDERSEQPRPERTY)) {
			String seqProperty=meta.getAttr(Meta.ORDERSEQPRPERTY, "seq");
			String parentIdProperty=meta.getAttr(Meta.PARENTIDPROPERTY, "fid");
			MField fidField=meta.getField(parentIdProperty);
			String fidColumn=fidField!=null?fidField.getAttr(MField.STORECOLUMN, fidField.getName()):"fid";
			Object seq=params.get(seqProperty);
			if(seq==null||StringUtils.isBlank(String.valueOf(seq))||objectMapper.convertValue(seq, Long.class)<=0) {
				StringBuilder seqSb=new StringBuilder("select count(0) as max from ")
						.append(meta.getName())
						.append(" where ")
						.append(fidColumn)
						.append("=?");
				Long pmetaId=meta.getMetaId();
				if(pmetaId.equals(Meta.MODULE_ROW_META)||pmetaId.equals(Meta.MODULE_COL_META)) {
					if(meta.containsKey(Meta.MODULE_PROPERTY)) {
						seqSb.append(" and ")
						.append(meta.getAttr(Meta.MODULE_PROPERTY,"rank"))
						.append("=").append(meta.getId());
					}
				}else if(pmetaId.equals(Meta.ENTITY_MODEL_META)||pmetaId.equals(Meta.TRAIT_MODEL_META)) {
					if(meta.containsKey(Meta.TYPEPROPERTY)) {
						seqSb.append(" and ")
						.append(meta.getAttr(Meta.TYPEPROPERTY,"meta"))
						.append("=").append(meta.getId());
					}
				}
				
				Long nextseq=jdbcTemplate.queryForObject(seqSb.toString(),new Object[] {params.get(parentIdProperty)}, Long.class);
				params.put(seqProperty, nextseq);
			}
		}
		MField pkField=meta.getPKField();
		List<MField> fs=meta.getFields();
		StringBuilder sb=new StringBuilder("insert into ")
				.append(meta.getName())
				.append("(");
		StringBuilder valuesb=new StringBuilder(")values(");
		List<Object> args=new ArrayList<>();
		for(MField f:fs) {
			if(f!=pkField) {
				Object v=params.get(f.getName());
				if(v==null) {
					v=f.getAttrs().get(MField.DEFAULT_VALUE);
				}
				try {
				v=DataConverter.convert(v, f.getType());
				}catch(Exception e) {
					v=null;
				}
				args.add(v);
			}else {
				String seqName=(String) pkField.getAttr(MField.FORMULA,meta.getAttr(Meta.IDSEQGENERATOR, "seq_sid"));
				Long sid=jdbcTemplate.queryForObject("select nextval(?) as sid",new Object[] {seqName}, Long.class);
				args.add(sid);
			}
			String storeColumn=(String) f.getAttr(MField.STORECOLUMN, f.getName());
			sb.append(storeColumn).append(",");
			valuesb.append("?,");
		}
		if(meta.containsKey(Meta.TYPEPROPERTY)) {
			String metaColumn=(String) meta.get(Meta.TYPEPROPERTY);
			if(!params.containsKey(metaColumn)) {
				sb.append(metaColumn).append(",");
				valuesb.append("?,");
				args.add(meta.getId());
			}
		}
		
		sb.setLength(sb.length()-1);
		valuesb.setLength(valuesb.length()-1);
		sb.append(valuesb).append(")");
		return jdbcTemplate.update(sb.toString(),args.toArray());
	}

	@Override
	public int updateRecord(Meta meta, Map<String, Object> record) {
		Long metaId=meta.getMetaId();
		if(metaId.equals(Meta.MODULE_COL_META)) {
			Long id=objectMapper.convertValue(record.get("id"), Long.class);
			record.remove("id");
			String table=meta.getName();
			String idColumn="sid";
			String typeProperty=meta.getAttr(Meta.TYPEPROPERTY, "meta");
			String parentIdproperty=meta.getAttr(Meta.PARENTIDPROPERTY, "fid");
			String keyColumn=meta.getAttr(Meta.KEY_COLUMN, "name");
			String valueColumn=meta.getAttr(Meta.VALUE_COLUMN, "code");
			String moduleColumn=meta.getAttr(Meta.MODULE_PROPERTY, "rank");
			Map<String,MField> fm=meta.getNameFieldMap();
			StringBuilder sb=new StringBuilder("select * from ")
					.append(table)
					.append(" where ")
					.append(parentIdproperty)
					.append("=? and ")
					.append(keyColumn)
					.append(" in('")
					.append(StringUtils.join(record.keySet(),"','"))
					.append("')");
			if(meta.containsKey(Meta.MODULE_PROPERTY)) {
				sb.append( " and ")
					.append(meta.getAttr(Meta.MODULE_PROPERTY,"rank"))
					.append("=")
					.append(meta.getId())
					.toString();
			}
			String sql=sb.toString();
			List<Map<String,Object>> list=jdbcTemplate.queryForList(sql, new Object[] {id});
			Map<String,Map<String,Object>> gs=new HashMap<>();
			for(Map<String,Object> n:list) {
				String key=(String)n.get(keyColumn);
				gs.put(key, n);
			}
			String insertSql=new StringBuilder("insert into ")
					.append(table)
					.append("(")
					.append(idColumn)
					.append(",")
					.append(parentIdproperty)
					.append(",")
					.append(keyColumn)
					.append(",")
					.append(valueColumn)
					.append(",")
					.append(moduleColumn)
					.append(",")
					.append(typeProperty)
					.append(",flag,tims) values(nextval(?),?,?,?,?,?,1,now())")
					.toString();
			String idSeqName=meta.getAttr(Meta.IDSEQGENERATOR, "seq_sid");
			for(Map.Entry<String, Object> e:record.entrySet()) {
				String key=e.getKey();
				String value=Objects.toString(e.getValue(), "");
				Map<String,Object> r=gs.get(key);
				if(r==null) {
					if(StringUtils.isNotBlank(value)) {
						MField f=fm.get(key);
						jdbcTemplate.update(insertSql,idSeqName,id,key,value,meta.getId(),f.getFid());
					}
					
				}else {
					updateValueBySid(table,idColumn,valueColumn,objectMapper.convertValue(r.get("sid"), Long.class),value);
					
				}
			}
			return 1;
			
		}
		StringBuilder sb=new StringBuilder("update ")
				.append(meta.getName())
				.append(" set ");
		List<Object> args=new ArrayList<>();
		for(Map.Entry<String, Object> e:record.entrySet()) {
			String name=e.getKey();
			MField f=meta.getField(name);
			Object v=e.getValue();
			v=DataConverter.convert(v, f.getType());
			sb.append(f.getAttr(MField.STORECOLUMN, f.getName()))
			.append("=?,");
			args.add(v);
		}
		MField pkField=meta.getPKField();
		String pkColumn=(String) pkField.getAttr(MField.STORECOLUMN, pkField.getName());
		
		sb.setLength(sb.length()-1);
		sb.append(" where ")
		.append(pkColumn)
		.append("=?");
		args.add(record.get(pkField.getName()));
		return jdbcTemplate.update(sb.toString(),args.toArray());
	}
	

	@Override
	public Map<String, Object> query(Meta meta, Map<String, Object> filterMap) {
		Long pmetaId=meta.getMetaId();
		if(pmetaId.equals(Meta.TRAIT_MODEL_META)) {
			Long storeModuleId=meta.getAttr(Meta.STORE_RANK,meta.getFid());
			Meta pmeta=getMetaById(storeModuleId);
			if(pmeta.getMetaId().equals(Meta.MODULE_COL_META)) {
				String parentIdProperty=pmeta.getAttr(Meta.PARENTIDPROPERTY, Meta.PARENTIDPROPERTY_DEFAULT);
				MField fidField=pmeta.getField(parentIdProperty);
				String fidColumn=fidField!=null?fidField.getAttr(MField.STORECOLUMN, fidField.getName()):"fid";
				String keyColumn=pmeta.getAttr(Meta.KEY_COLUMN, "name");
				String valueColumn=pmeta.getAttr(Meta.VALUE_COLUMN, "code");
				Map<String,Object> rs=new HashMap<>();
				String sql=new StringBuilder("select * from ")
						.append(meta.getName())
						.append(" where ")
						.append(fidColumn)
						.append("=?")
						.toString();
				List<Map<String,Object>> list=jdbcTemplate.queryForList(sql,filterMap.get("id"));
				if(CollectionUtils.isNotEmpty(list)) {
					for(Map<String,Object> n:list) {
						String key=(String) n.get(keyColumn);
						String value=(String)n.get(valueColumn);
						rs.put(key, value);
					}
				}
				return rs;
			}else {
				String parentIdProperty=pmeta.getAttr(Meta.PARENTIDPROPERTY, Meta.PARENTIDPROPERTY_DEFAULT);
//				MField fidField=pmeta.getField(parentIdProperty);
//				String fidColumn=fidField!=null?fidField.getAttr(MField.STORECOLUMN,fidField.getName()):"fid";
				StringBuilder sb=new StringBuilder("select ");
				List<MField> fs=meta.getFields();
				if(CollectionUtils.isNotEmpty(fs)) {
					for(MField f:fs) {
						sb.append(f.getAttr(MField.STORECOLUMN, f.getName()))
						.append(" AS '").append(f.getName()).append("',");
					}
					sb.setLength(sb.length()-1);
				}else{
					sb.append("*");
				}
				sb.append(" from ").append(meta.getName());
				List<Object> args=new ArrayList<>();
				String sql=getFilterSql(sb,meta,filterMap,args);
				return jdbcTemplate.queryForMap(sql,args.toArray());
			}
			
			
		}else if(pmetaId.equals(Meta.MODULE_COL_META)) {
			String parentIdProperty=meta.getAttr(Meta.PARENTIDPROPERTY, Meta.PARENTIDPROPERTY_DEFAULT);
			MField fidField=meta.getField(parentIdProperty);
			String fidColumn=fidField!=null?fidField.getAttr(MField.STORECOLUMN, fidField.getName()):"fid";
			String keyColumn=meta.getAttr(Meta.KEY_COLUMN, "name");
			String valueColumn=meta.getAttr(Meta.VALUE_COLUMN, "code");
			
			Map<String,Object> rs=new HashMap<>();
			StringBuilder sb=new StringBuilder("select * from ")
					.append(meta.getName())
					.append(" where ")
					.append(fidColumn)
					.append("=?");
			if(meta.containsKey(Meta.MODULE_PROPERTY)) {
				String moduleProperty=meta.getAttr(Meta.MODULE_PROPERTY, "rank");
				sb.append(moduleProperty).append("=")
				.append(meta.getId());
			}
			String sql=sb.toString();
			List<Map<String,Object>> list=jdbcTemplate.queryForList(sql,filterMap.get("id"));
			if(CollectionUtils.isNotEmpty(list)) {
				for(Map<String,Object> n:list) {
					String key=(String) n.get(keyColumn);
					String value=(String)n.get(valueColumn);
					rs.put(key, value);
				}
			}
			return rs;
		}
		
		else {
			StringBuilder sb=new StringBuilder("select ");
			List<MField> fs=meta.getFields();
			if(CollectionUtils.isNotEmpty(fs)) {
				for(MField f:fs) {
					sb.append(f.getAttr(MField.STORECOLUMN, f.getName()))
					.append(" AS '").append(f.getName()).append("',");
				}
				sb.setLength(sb.length()-1);
			}else{
				sb.append("*");
			}
			sb.append(" from ").append(meta.getName());
			List<Object> args=new ArrayList<>();
			String sql=getFilterSql(sb,meta,filterMap,args);
			return jdbcTemplate.queryForMap(sql,args.toArray());
		}
		
	}

	@Override
	public int saveTraits(long id, Map<String, Object> data, long metaId) {
		Meta meta=getMetaById(metaId);
		String table=meta.getName();
		String idColumn="sid";
		String typeProperty=meta.getAttr(Meta.TYPEPROPERTY, "meta");
		String parentIdproperty=meta.getAttr(Meta.PARENTIDPROPERTY, "fid");
		String keyColumn=meta.getAttr(Meta.KEY_COLUMN, "name");
		String valueColumn=meta.getAttr(Meta.VALUE_COLUMN, "code");
		String moduleColumn=meta.getAttr(Meta.MODULE_PROPERTY, "rank");
		String sql=new StringBuilder("select * from ")
				.append(table)
				.append(" where ")
				.append(parentIdproperty)
				.append("=? and ")
				.append(keyColumn)
				.append(" in('")
				.append(StringUtils.join(data.keySet(),"','"))
				.append("') and ")
				.append(typeProperty)
				.append("=?")
				.toString();
		List<Map<String,Object>> list=jdbcTemplate.queryForList(sql, new Object[] {id,metaId});
		Map<String,Map<String,Object>> gs=new HashMap<>();
		for(Map<String,Object> n:list) {
			String key=(String)n.get(keyColumn);
			gs.put(key, n);
		}
		String insertSql=new StringBuilder("insert into ")
				.append(table)
				.append("(")
				.append(idColumn)
				.append(",")
				.append(parentIdproperty)
				.append(",")
				.append(keyColumn)
				.append(",")
				.append(valueColumn)
				.append(",")
				.append(moduleColumn)
				.append(",")
				.append(typeProperty)
				.append(",flag,tims) values(nextval('seq_sid'),?,?,?,?,?,1,now())")
				.toString();
		data.remove("id");
		for(Map.Entry<String, Object> e:data.entrySet()) {
			String key=e.getKey();
			String value=Objects.toString(e.getValue(), "");
			Map<String,Object> record=gs.get(key);
			if(record==null) {
				if(StringUtils.isNotBlank(value))
					jdbcTemplate.update(insertSql,id,key,value,meta.getFid(),metaId);
			}else {
				updateValueBySid(table,idColumn,valueColumn,objectMapper.convertValue(record.get(idColumn), Long.class),value);
				
			}
		}
		return 1;
	}
	
	
	
	private void updateValueBySid(String table,String idColumn,String valueColumn,Long sid,String value) {
		if(StringUtils.isBlank(value)) {
			String sql=new StringBuilder("delete from ")
					.append(table)
					.append(" where ")
					.append(idColumn)
					.append("=?").toString();
			jdbcTemplate.update(sql,sid);
		}else {
			String sql=new StringBuilder("update ")
					.append(table)
					.append(" set ")
					.append(valueColumn)
					.append("=? where ")
					.append(idColumn)
					.append("=?").toString();
			jdbcTemplate.update(sql,value,sid);
		}
	}

	@Override
	public Collection<Long> getSubMetas(Long id) {
		List<Meta> cs=metaDAO.findByFidOrderBySeq(id);
		if(CollectionUtils.isEmpty(cs)) {
			return Collections.emptyList();
		}
		List<Long> rs=new ArrayList<>();
		for(Meta m:cs) {
			rs.add(m.getId());
		}
		
		return rs;
	}

	@Override
	public Map<String, Object> getRecord(Meta meta, Long id) {
		Long metaId=meta.getId();
		Long pmetaId=meta.getMetaId();
		if(pmetaId.equals(Meta.TRAIT_MODEL_META)) {
			Long storeMetaId=meta.getAttr(Meta.STORE_RANK, meta.getFid());
			Meta storeModuleMeta=metaDAO.findOne(storeMetaId);
			if(storeModuleMeta.getMetaId().equals(Meta.MODULE_COL_META)) {
				String parentIdProperty=storeModuleMeta.getAttr(Meta.PARENTIDPROPERTY, "fid");
				String typeProperty=storeModuleMeta.getAttr(Meta.TYPEPROPERTY, "metaId");
				MField metaField=storeModuleMeta.getField(typeProperty);
				String metaColumn=metaField!=null?metaField.getAttr(MField.STORECOLUMN, metaField.getName()):"meta";
				String keyColumn=storeModuleMeta.getAttr(Meta.KEY_COLUMN, "name");
				String valueColumn=storeModuleMeta.getAttr(Meta.VALUE_COLUMN,"code");
				StringBuilder sb=new StringBuilder("select * from ")
						.append(storeModuleMeta.getName())
						.append(" where ")
						.append(parentIdProperty)
						.append("=? and ")
						.append(metaColumn)
						.append("=")
						.append(metaId);
				List<Map<String,Object>> list=jdbcTemplate.queryForList(sb.toString(), id);
				Map<String,Object> rs=new HashMap<>();
				Map<String,MField> fm=meta.getNameFieldMap();
				
				for(Map<String,Object> n:list) {
					String name=(String) n.get(keyColumn);
					String value=(String) n.get(valueColumn);
					MField f=fm.get(name);
					if(f!=null) {
						Object v=DataConverter.convert(value, f.getType());
						rs.put(name, v);
					}else {
						if(StringUtils.contains(name, ".")) {
							MapUtil.putExtendedValue(rs, name, value);
						}
					}
					
				}
				return rs;
			}else {
				String parentIdProperty=meta.getAttr(Meta.PARENTIDPROPERTY, "fid");
				MField fidField=meta.getField(parentIdProperty);
				String fidColumn=fidField!=null?fidField.getAttr(MField.STORECOLUMN, "fid"):"fid";
				List<MField> fs=meta.getFields();
				StringBuilder sb=new StringBuilder("select ");
				for(MField f:fs) {
					sb.append(f.getAttr(MField.STORECOLUMN, f.getName()))
					.append(" AS '")
					.append(f.getName())
					.append("',");
				}
				sb.setLength(sb.length()-1);
				sb.append(" from ")
						.append(meta.getName()).append(" where ")
						.append(fidColumn)
						.append("=?");
				if(storeModuleMeta.containsKey(Meta.TYPEPROPERTY)) {
					sb.append(" and ")
					.append(storeModuleMeta.getAttr(Meta.TYPEPROPERTY))
					.append("=")
					.append(metaId);
				}
				return jdbcTemplate.queryForMap(sb.toString(),id);
			}
		}else if(pmetaId.equals(Meta.MODULE_COL_META)) {
			Map<String,MField> fm=meta.getNameFieldMap();
			String parentIdProperty=meta.getAttr(Meta.PARENTIDPROPERTY, "fid");
			String moduleProperty=meta.getAttr(Meta.MODULE_PROPERTY, "rank");
			String keyColumn=meta.getAttr(Meta.KEY_COLUMN, "name");
			String valueColumn=meta.getAttr(Meta.VALUE_COLUMN,"code");
			StringBuilder sb=new StringBuilder("select * from ")
					.append(meta.getName())
					.append(" where ")
					.append(parentIdProperty)
					.append("=? and ")
					.append(moduleProperty)
					.append("=")
					.append(metaId);
			List<Map<String,Object>> list=jdbcTemplate.queryForList(sb.toString(), id);
			Map<String,Object> rs=new HashMap<>();
			for(Map<String,Object> n:list) {
				String name=(String) n.get(keyColumn);
				String value=(String) n.get(valueColumn);
				MField f=fm.get(name);
				Object v=DataConverter.convert(value, f.getType());
				rs.put(name, v);
			}
			return rs;
		}
		String idProperty=meta.getAttr(Meta.IDPROPERTY, "id");
		MField idField=meta.getField(idProperty);
		String idColumn=idField!=null?idField.getAttr(MField.STORECOLUMN, "sid"):"sid";
		List<MField> fs=meta.getFields();
		StringBuilder sb=new StringBuilder("select ");
		for(MField f:fs) {
			sb.append(f.getAttr(MField.STORECOLUMN, f.getName()))
			.append(" AS '")
			.append(f.getName())
			.append("',");
		}
		sb.setLength(sb.length()-1);
		String sql=sb.append(" from ")
				.append(meta.getName()).append(" where ")
				.append(idColumn)
				.append("=?").toString();
		
		return jdbcTemplate.queryForMap(sql,id);
	}

	@Override
	public List<Map<String, Object>> getChildren(Meta meta, Long id) {
		Long pmetaId=meta.getMetaId();
		String typeProperty=(String) meta.getAttr(Meta.TYPEPROPERTY);
		String parentIdProperty=meta.getAttr(Meta.PARENTIDPROPERTY,"fid");
		MField field=meta.getField(parentIdProperty);
		
		String fidColumn=field.getAttr(MField.STORECOLUMN, field.getName());
		StringBuilder sb=new StringBuilder("select  ");
		List<MField> fs=meta.getFields();
		for(MField f:fs) {
			sb.append(f.getAttr(MField.STORECOLUMN,f.getName())).append(" as '")
			.append(f.getName())
			.append("',");
		}
		sb.setLength(sb.length()-1);
		sb.append(" from ").append(meta.getName())
		.append(" where ")
		.append(fidColumn).append("=?");
		if(pmetaId.equals(Meta.MODULE_ROW_META)) {
			String moduleProperty=meta.getAttr(Meta.MODULE_PROPERTY, "rank");
			MField moduleField=meta.getField(moduleProperty);
			String moduleColumn=moduleField!=null?moduleField.getAttr(MField.STORECOLUMN, moduleField.getName()):"rank";
			sb.append(" and ")
			.append(moduleColumn)
			.append("=").append(meta.getId());
		}else {
			MField typeField=meta.getField(typeProperty);
			String metaColumn=typeField!=null?typeField.getAttr(MField.STORECOLUMN, typeField.getName()):"meta";
			sb.append(" and ")
			.append(metaColumn)
			.append("=").append(meta.getId());
		}
		if(meta.containsKey(Meta.ORDERSEQPRPERTY)) {
			String seqProperty=(String) meta.getAttr(Meta.ORDERSEQPRPERTY);
			MField seqField=meta.getField(seqProperty);
			seqProperty=seqField!=null?seqField.getAttr(MField.STORECOLUMN, seqField.getName()):seqProperty;
			sb.append(" order by ")
			.append(seqProperty)
			.append(" asc");
		}
		String sql=sb.toString();
		return jdbcTemplate.queryForList(sql, id);
	}

	@Override
	public List<Map<String, Object>> getChildren(Long metaId, Long id) {
		Meta meta=getMetaById(metaId);
		return getChildren(meta,id);
	}

	@Override
	public List<Map<String, Object>> getAllChildren(Long metaId, Long id) {
		Meta meta=getMetaById(metaId);
		List<Map<String,Object>> list= getChildren(meta,id);
		if(CollectionUtils.isNotEmpty(list)) {
			String idProperty=meta.getAttr(Meta.IDPROPERTY, "id");
			for(Map<String,Object> n:list) {
				Long fid=((Number)n.get(idProperty)).longValue();
				List<Map<String,Object>> cs=getAllChildren(metaId,fid);
				if(CollectionUtils.isNotEmpty(cs)) {
					n.put("children", cs);
				}
			}
		}
		return list;
	}

	@Override
	public List<MField> getFields(long id, int rankId) {
		return fieldDAO.findByFidAndRankOrderBySeq(id, rankId);
	}

	@Override
	public Map<String, Object> getAttrs(Long id, int rankId) {
		List<Attr> list=attrDAO.findByFidAndRank(id, rankId);
		Map<String,Object> attrs=new HashMap<>();
		for(Attr attr:list) {
			Long metaId=attr.getMeta();
			String key=attr.getName();
			if(metaId!=null) {
				Meta attrMeta=getMetaById(metaId);
				if(attrMeta!=null) {
					MField f=attrMeta.getField(attr.getName());
					if(f!=null) {
						Object v=DataConverter.convert(attr.getCode(), f.getType());
						MapUtil.putExtendedValue(attrs, f.getName(), v);
					}else {
						String value=attr.getCode();
						if(StringUtils.isNumeric(value)) {
							MapUtil.putExtendedValue(attrs, key, Long.parseLong(value));
						}else {
							MapUtil.putExtendedValue(attrs, key, attr.getCode());
						}
						
					}
					
				}
			}else {
				MapUtil.putExtendedValue(attrs, key, attr.getCode());
			}
			
		}
		return attrs;
	}
	
}
